Skip to content

feat: add a deny list for entities in Anonymizer - #243

Open
memadi-nv wants to merge 15 commits into
mainfrom
memadi/feature/add-entity-deny-list
Open

feat: add a deny list for entities in Anonymizer#243
memadi-nv wants to merge 15 commits into
mainfrom
memadi/feature/add-entity-deny-list

Conversation

@memadi-nv

@memadi-nv memadi-nv commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds entity_label_denylist to the Detect configuration, allowing users to explicitly exclude specific entity label types from detection. Denied labels are never detected, augmented, or penalised in evaluation scores.

Changes

Config

  • Added entity_label_denylist: list[str] | None field to Detect with the same normalisation as entity_labels (strip, lowercase, deduplicate, reject empty)
  • Added model validator that warns at config construction time when entity_labels and entity_label_denylist share labels that would never be detected.

Detection Engine

  • Subtracted denied labels from the effective label set before GLiNER and LLM prompts run (_resolve_detection_labels)
  • Added safety-net filter in _materialize_final_entities to drop denied labels from final entity output regardless of LLM output
  • Threaded entity_label_denylist through all detection pipeline methods: run, detect_and_validate_entities, _build_detection_spec, identify_latent_entities
  • Threaded through export paths: build_detection_config and build_detection_builder_for_seed
  • Added warning when the denylist empties the effective label set (GLiNER returns no detections for an empty list, not defaults)

Evaluation

  • Added entity_label_denylist field to AnonymizerResult and PreviewResult so evaluate() has access to the full detection scope
  • Propagated entity_label_denylist through evaluate() to EntityCoverageWorkflow and ReplacementWorkflow.evaluate()
  • Added _effective_entity_labels() helper that computes entity_labels − entity_label_denylist and applied it to both the coverage judge LLM prompt and the postprocess filter

Telemetry

  • Added entity_label_denylist to _detect_config_metadata so it is captured in measurement records alongside entity_labels

Tests

  • Config validation: field normalisation, empty list rejection, overlap warning
  • Detection engine: label resolution, GLiNER injection, final entity filtering, combined allowlist + denylist, export paths
  • Evaluation: _effective_entity_labels helper, coverage judge postprocess filter, coverage prompt scoping
  • Telemetry: entity_label_denylist captured correctly when set and absent

Docs

  • Updated Detect fields reference table and added "Excluding labels with a deny list" section in detection.md
  • Added entity_label_denylist to the judge scoping list in evaluation.md
  • Added entity_label_denylist section and corrected the data_summary soft-exclusion claim in choosing-a-strategy.md
  • Added entity_label_denylist as the recommended fix for systematically noisy label types in troubleshooting.md
  • Updated skills/anonymizer/SKILL.md with denylist behavior and precedence rule

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring
  • CI, release, or contributor workflow update

Contributor Checklist

  • PR title follows Conventional Commits, for example fix: handle empty entity list
  • Related issue is linked, or a maintainer-owned no-issue reason is documented above
  • For non-trivial changes, a plan document is linked above, or the no-plan reason is documented above
  • Public API impact checked; skills/anonymizer/SKILL.md updated if needed
  • No real PII added to tests, docs, notebooks, fixtures, or artifacts
  • No API keys, service tokens, private keys, credentials, or real endpoint secrets added

Validation

  • Commands run:
  • Skipped checks or known failures:

Documentation and Artifacts

  • Docs updated, or not needed
  • If docs changed: make docs-build passes locally
  • If tutorial sources changed: notebooks regenerated with make convert-notebooks
  • If e2e, benchmark, or model-provider behavior changed: relevant validation is listed above

closes issue #164

memadi-nv and others added 10 commits August 10, 2026 16:52
Adds entity_label_denylist: list[str] | None to the Detect model, with
the same normalisation (strip, lowercase, deduplicate) as entity_labels.
A model_validator warns at config construction time when entity_labels
and entity_label_denylist share labels that would never be detected.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Applies the denylist at two points:
- _resolve_detection_labels: subtracts denied labels before they reach
  GLiNER and the LLM augmenter/validator prompts
- _materialize_final_entities: safety-net filter that drops any entity
  whose label is in the denylist from COL_FINAL_ENTITIES

Threads entity_label_denylist through detect_and_validate_entities,
_build_detection_spec, identify_latent_entities, and run on
EntityDetectionWorkflow, and wires it from Anonymizer._run_internal
via config.detect.entity_label_denylist.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…ion paths

Adds tests for build_detection_config and build_detection_builder_for_seed
confirming that denied labels are subtracted from the GLiNER label list in
the serialized workflow config, so external runtimes see the same effective
label set as the in-process path.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…abel set

When the denylist subtracts all labels from the effective detection set,
_resolve_detection_labels now emits a warning instead of silently passing
an empty list to GLiNER (which returns no detections, not the default set).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…peline

Stores entity_label_denylist on AnonymizerResult and PreviewResult so
evaluate() can scope its judges to the same label set that was active
during detection.

Changes:
- AnonymizerResult / PreviewResult: add entity_label_denylist field
- Anonymizer.evaluate(): extract entity_label_denylist from the result
  and pass it to EntityCoverageWorkflow (rewrite path) and
  ReplacementWorkflow.evaluate() (replace path)
- EntityCoverageWorkflow: accept entity_label_denylist, pass to
  _filter_out_of_scope_entities in postprocess
- _filter_out_of_scope_entities: exclude entities whose label is in the
  denylist so the judge does not penalise the output for not anonymizing
  denied labels
- ReplacementWorkflow.evaluate(): thread entity_label_denylist through
  to EntityCoverageWorkflow

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…filter

Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
@memadi-nv
memadi-nv requested review from a team as code owners August 11, 2026 01:02
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a normalized entity-label denylist and propagates it through detection, rewrite, evaluation, result metadata, measurement, documentation, and tests.

  • Excludes denied labels from detector inputs and final detected entities.
  • Filters denied latent entities before rewrite sensitivity analysis.
  • Keeps permissive evaluation open to novel labels while excluding denied types.
  • Preserves denylist context for later evaluation and measurement.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the current code fixes the previously reported evaluation, case-normalization, whitespace-normalization, and latent-entity denylist issues.

Important Files Changed

Filename Overview
src/anonymizer/config/anonymizer_config.py Adds normalized, deduplicated denylist configuration and overlap warnings.
src/anonymizer/engine/detection/detection_workflow.py Applies the denylist to detector labels, final entities, latent prompts, and latent structured output, resolving the previously reported bypasses.
src/anonymizer/engine/evaluation/entity_coverage_judge.py Preserves permissive evaluation for novel labels while independently excluding denied labels in the prompt and deterministic filtering.
src/anonymizer/interface/anonymizer.py Propagates denylist configuration across run, preview, export, rewrite, replacement, and evaluation paths.
src/anonymizer/interface/results.py Retains denylist context on result objects for later evaluation.
src/anonymizer/measurement/records/run.py Adds normalized denylist metadata to sanitized run measurements.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    C[Detect configuration] --> R[Resolve effective labels]
    R --> D[GLiNER and detection prompts]
    D --> F[Normalize and filter final entities]
    D --> L[Latent entity detection]
    L --> LF[Filter denied latent entities]
    F --> A[Replace or rewrite]
    LF --> A
    A --> E[Coverage evaluation]
    C --> E
    E --> O[Result and measurement metadata]
Loading

Reviews (6): Last reviewed commit: "nit" | Re-trigger Greptile

Comment thread src/anonymizer/engine/evaluation/entity_coverage_judge.py Outdated
Comment thread src/anonymizer/engine/detection/detection_workflow.py Outdated
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
Comment thread src/anonymizer/engine/detection/detection_workflow.py
Signed-off-by: memadi <memadi@nvidia.com>
Comment thread src/anonymizer/engine/detection/detection_workflow.py Outdated
memadi-nv and others added 2 commits August 11, 2026 17:01
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: memadi <memadi@nvidia.com>
@memadi-nv

Copy link
Copy Markdown
Contributor Author

/nvskills-ci

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant